package net.yegong.util.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

import net.yegong.util.Pair;

/**
 * The Reflect offers several convenience method to access reflect of objects.
 * 
 * @author cooper
 */
public class Reflect {

	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramsClazz) throws ReflectionException {
		Exception err = null;
		try {
			return clazz.getDeclaredMethod(methodName, paramsClazz);
		}
		catch (SecurityException e) {
			err = e;
		}
		catch (NoSuchMethodException e) {
			err = e;
		}
		try {
			paramsClazz = TypeTraits.toPrimitive(paramsClazz);
			return clazz.getDeclaredMethod(methodName, paramsClazz);
		}
		catch (SecurityException e) {
			err = e;
		}
		catch (NoSuchMethodException e) {
			err = e;
		}
		try {
			paramsClazz = TypeTraits.toWrapper(paramsClazz);
			return clazz.getDeclaredMethod(methodName, paramsClazz);
		}
		catch (SecurityException e) {
			err = e;
		}
		catch (NoSuchMethodException e) {
			err = e;
		}
		throw new ReflectionException(err);
	}

	public static Object invoke(Method method, Object... params) throws ReflectionException {
		return invoke(null, method, params);
	}

	public static Object invoke(Object target, Method method, Object... params) throws ReflectionException {
		try {
			method.setAccessible(true);
			return method.invoke(target, params);
		}
		catch (SecurityException e) {
			throw new ReflectionException(e);
		}
		catch (IllegalArgumentException e) {
			throw new ReflectionException(e);
		}
		catch (IllegalAccessException e) {
			throw new ReflectionException(e);
		}
		catch (InvocationTargetException e) {
			throw new ReflectionException(e);
		}
		finally {
			method.setAccessible(false);
		}
	}

	public static Field getField(Class<?> clazz, String fieldName) throws ReflectionException {
		try {
			return clazz.getDeclaredField(fieldName);
		}
		catch (SecurityException e) {
			throw new ReflectionException(e);
		}
		catch (NoSuchFieldException e) {
			throw new ReflectionException(e);
		}
	}

	public static void set(Class<?> clazz, String fieldName, String value) throws ReflectionException {
		Field field = getField(clazz, fieldName);
		set(null, field, value);
	}

	public static void set(Object target, String fieldName, String value) throws ReflectionException {
		Class<?> clazz = target.getClass();
		set(target, clazz, fieldName, value);
	}

	public static void set(Object target, Class<?> clazz, String fieldName, String value) throws ReflectionException {
		Field field = getField(clazz, fieldName);
		set(target, field, value);
	}

	public static void set(Field field, String value) throws ReflectionException {
		set(null, field, value);
	}

	@SuppressWarnings("unchecked")
	public static void set(Object target, Field field, String value) throws ReflectionException {
		try {
			field.setAccessible(true);
			Type fieldType = field.getGenericType();
			Class<?> fieldClazz = field.getType();
			Object valueObject = LexicalCast.cast(value, fieldType);
			if (Collection.class.isAssignableFrom(fieldClazz)) {
				Object object = field.get(target);
				if (object == null) {
					object = AbstractObjectFactory.newInstanceOf(fieldClazz);
				}
				Collection<Object> coll = (Collection<Object>) object;
				coll.add(valueObject);
				field.set(target, coll);
				return;
			}
			if (Map.class.isAssignableFrom(fieldClazz)) {
				Pair<Object, Object> pair = (Pair<Object, Object>) valueObject;
				Object object = field.get(target);
				if (object == null) {
					object = AbstractObjectFactory.newInstanceOf(fieldClazz);
				}
				Map<Object, Object> map = (Map<Object, Object>) object;
				map.put(pair.first(), pair.second());
				field.set(target, map);
				return;
			}
			field.set(target, valueObject);
		}
		catch (IllegalArgumentException e) {
			throw new ReflectionException(e);
		}
		catch (IllegalAccessException e) {
			throw new ReflectionException(e);
		}
		catch (SecurityException e) {
			throw new ReflectionException(e);
		}
		finally {
			try {
				field.setAccessible(false);
			}
			catch (Exception e) {
			}
		}
	}

	public static Object get(Class<?> clazz, String fieldName) throws ReflectionException {
		Field field = getField(clazz, fieldName);
		return get(null, field);
	}

	public static Object get(Object target, String fieldName) throws ReflectionException {
		Class<?> clazz = target.getClass();
		return get(target, clazz, fieldName);
	}

	public static Object get(Object target, Class<?> clazz, String fieldName) throws ReflectionException {
		Field field = getField(clazz, fieldName);
		return get(target, field);
	}

	public static Object get(Field field) throws ReflectionException {
		return get(null, field);
	}

	public static Object get(Object target, Field field) throws ReflectionException {
		try {
			field.setAccessible(true);
			return field.get(target);
		}
		catch (IllegalArgumentException e) {
			throw new ReflectionException(e);
		}
		catch (IllegalAccessException e) {
			throw new ReflectionException(e);
		}
		catch (SecurityException e) {
			throw new ReflectionException(e);
		}
		finally {
			try {
				field.setAccessible(false);
			}
			catch (Exception e) {
			}
		}
	}
}
